package com.g2.admin.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.g2.admin.common.constant.Constants;
import com.g2.admin.common.constant.RedisConstants;
import com.g2.admin.common.constant.StatusTypeEnums;
import com.g2.admin.model.po.AuthAuthorityPo;
import com.g2.admin.model.po.AuthPermitUserPo;
import com.g2.admin.model.qo.AccountQo;
import com.g2.admin.model.to.ResultTo;
import com.g2.admin.utils.GenUtil;
import com.g2.admin.utils.RedisUtil;
import com.g2.admin.utils.RegularUtil;
import com.g2.admin.utils.SecretUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.g2.admin.model.po.UserAccountPo;
import com.g2.admin.dao.mapper.IUserAccountMapper;
import org.springframework.util.Assert;

import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Service
@Slf4j
public class UserAccountService {
    @Autowired
    private AuthAuthorityService authorityService;
    @Autowired
    private AuthPermitUserService authPermitUserService;
    @Autowired
    private AuthRoleUserService authRoleUserService;
    @Autowired
    private IUserAccountMapper userAccountMapper;
    @Autowired
    private RedisUtil<UserAccountPo> redisUtil;
    @Autowired
    private RedisUtil<Pattern> patternRedisUtil;

    public UserAccountPo getUserAccount(long id) {
        return userAccountMapper.selectById(id);
    }

    public void delUserAccount(long id) {
        userAccountMapper.deleteById(id);
    }

    public void addUserAccount(UserAccountPo userAccount) {
        // 判断用户是否存在
        int rs = userAccountMapper.selectCount(new QueryWrapper<UserAccountPo>().lambda().eq(UserAccountPo::getUsername, userAccount.getUsername()));
        Assert.isTrue(rs == 0, ResultTo.getCodeAndMsg(ResultTo.ResultCode.ACC_USER_EXIST));
        userAccount.setStatus(StatusTypeEnums.StatusEnums.ENABLE);
        userAccountMapper.insert(userAccount);
        userAccount.setSlat(SecretUtil.getRandomStr(6));
        userAccount.setPassword(SecretUtil.encryptPwd(userAccount.getPassword(), userAccount.getSlat()));
        Assert.isTrue(userAccountMapper.insert(userAccount) == 1, ResultTo.getCodeAndMsg(ResultTo.ResultCode.FAIL, "用户注册失败"));
    }

    public void updateUserAccountById(UserAccountPo useraccount) {
        int rs = userAccountMapper.updateById(useraccount);
        Assert.isTrue(rs == 1, ResultTo.ResultCode.FAIL.name());
    }

    public UserAccountPo getAccountByUsername(String username) {
        return userAccountMapper.selectOne(new QueryWrapper<UserAccountPo>().lambda().eq(UserAccountPo::getUsername, username));
    }

    /**
     * 用户登录
     *
     * @param username 用户名
     * @param password 密码
     * @return
     */
    public String login(String username, String password) {
        UserAccountPo userAccount = getAccountByUsername(username);
        Assert.notNull(userAccount, ResultTo.getCodeAndMsg(ResultTo.ResultCode.ACC_LOGIN_FAIL));
        Assert.isTrue(userAccount.getPassword().equals(SecretUtil.encryptPwd(password, userAccount.getSlat())), ResultTo.getCodeAndMsg(ResultTo.ResultCode.ACC_LOGIN_FAIL));
        Assert.isTrue(StatusTypeEnums.StatusEnums.ENABLE.equals(userAccount.getStatus()), ResultTo.getCodeAndMsg(ResultTo.ResultCode.ACC_USER_DISABLE));
        // 校验通过，生成token。为避免重复，加入了用户名密码作为MD5后的值
        String token = SecretUtil.md5(userAccount.getUsername() + userAccount.getId() + System.nanoTime() + GenUtil.getMac());
        // 获取权限信息并缓存
        getAuthPatterns(userAccount.getId(), token);
        UserAccountPo.cleanSensitiveInfo(userAccount);
        redisUtil.set(RedisConstants.getKeyToken(token), userAccount, RedisConstants.TIME_DEFAULT);
        return token;
    }

    public IPage<UserAccountPo> getAccountsAndPage(AccountQo accountQo) {
        QueryWrapper<UserAccountPo> qw = new QueryWrapper();
        qw.lambda()
                .select(UserAccountPo::getAvatar, UserAccountPo::getId, UserAccountPo::getName, UserAccountPo::getRemark,
                        UserAccountPo::getOrgId, UserAccountPo::getOrgName, UserAccountPo::getRelationId,
                        UserAccountPo::getStatus, UserAccountPo::getType, UserAccountPo::getUsername)
                .eq(null != accountQo.getOrgId(), UserAccountPo::getOrgId, accountQo.getOrgId())
                .like(StringUtils.isNotEmpty(accountQo.getName()), UserAccountPo::getName, accountQo.getName())
                .eq(StringUtils.isNotEmpty(accountQo.getUsername()), UserAccountPo::getUsername, accountQo.getUsername())
                .eq(null != accountQo.getStatus(), UserAccountPo::getStatus, accountQo.getStatus());
        return userAccountMapper.selectPage(new Page<>(accountQo.getPageNo(), accountQo.getSize()), qw);
    }

    public void updatePassword(UserAccountPo account) {
        UserAccountPo accountPo = getAccountByUsername(account.getUsername());
        Assert.notNull(accountPo, ResultTo.getCodeAndMsg(ResultTo.ResultCode.ACC_USER_NO));
        String slat = SecretUtil.getRandomStr(6);
        updateUserAccountById(UserAccountPo.builder().id(accountPo.getId()).slat(slat).password(SecretUtil.encryptPwd(account.getPassword(), slat)).build());
    }

    /**
     * 获取用户的授权访问路径的正则表达式列表。作为操作权限验证
     *
     * @param userId
     * @return
     */
    public void getAuthPatterns(long userId, String token) {
        // 获取单个权限
        List<Object> auths = authPermitUserService.getAuthsByUserId(userId);
        Set<String> authCodeSet = auths.stream().map(x -> String.valueOf(x)).collect(Collectors.toSet());
        // 获取角色权限
        List<String> authList = authRoleUserService.getUserRoleAuths(userId);
        authList.forEach(x -> {
            authCodeSet.add(x);
        });
        // 获取权限
        List<AuthAuthorityPo> authorityPos = authorityService.getAuthByCode(authCodeSet);
        if (null != authorityPos) {
            Set<Pattern> patternSet = Sets.newHashSet();
            authorityPos.stream().forEach(x -> {
                // 一个href字段含多个链接配置时，是以英文的逗号分隔
                String[] hrefs = x.getHref().split(Constants.COMMA_EN);
                for (String href : hrefs) {
                    patternSet.add(RegularUtil.getPathPattern(href));
                }
            });
            patternRedisUtil.sSet(RedisConstants.getKeyAuthPath(token), patternSet);
        }
    }

}
